#include "trafficview.h"
#include <QWheelEvent>
#include <QDebug>
#include "world.h"
#include "roaditem.h"
#include "intersectionitem.h"
#include "trafficcontrolitem.h"
#include "vehicleitem.h"
#include "streetmap.h"
#include "graphicsutilities.h"
#include "frame.h"


TrafficView::TrafficView(QWidget *parent)
  : QGraphicsView(parent)
{
  setScene(&mScene);
  mShowVectors=true;
  mShowForces=false;
  mZoomLevel = 1.0f;
  mShowLinesForSelectionOnly = false;
  mSelectedVehicle = NULL;

  show();
}

void TrafficView::refreshGraphics()
{
  World* w(World::instance());
  if (w)
  {
    Frame* f(w->mFrameManager.currentFrame());
    if (f)
    {
      foreach(VehicleFrame* vf, f->vehicles())
      {
        VehicleItem *vi(mVehicleItems.value(vf->name(), 0));
        if (!vi)
        {
          vi = new VehicleItem(vf->name(), QPixmap(vf->graphic()));
          vi->showVectors(mShowVectors);
          vi->showForces(mShowForces);
          vi->setFlag(QGraphicsItem::ItemIsSelectable);
          vi->setToolTip(vf->name());
          mVehicleItems[vf->name()] = vi;
          mScene.addItem(vi);
        }
        // Update vehicle graphics & effects
        vi->setVehicleFrame(vf);
      }

      foreach(TrafficControlFrame *tcf, f->trafficControlFrames())
      {
        TrafficControlItem *tci(mTrafficControlItems.value(tcf->name(), 0));
        if (!tci)
        {
          tci = new TrafficControlItem(tcf->intersection()->name());
          mTrafficControlItems[tcf->name()] = tci;
          mScene.addItem(tci);
        }
        tci->setFrame(tcf);
      }


      // Adjust sceneRect to fit all content
      setSceneRectWithMargin();
    }
  }
  else
  {
    clearGraphics();
  }
}

void TrafficView::rebuildMapGraphics()
{
  qDebug() << "rebuildMapGraphics";
  clearGraphics();
  World* w(World::instance());
  if (w && w->mStreetMap)
  {
    foreach(StreetMap::Road* r, w->mStreetMap->getRoads())
    {
      QGraphicsPathItem* road(createRoad(r));
      mScene.addItem(road);
    }

    foreach(StreetMap::Intersection* i, w->mStreetMap->getIntersections())
    {
      StreetMap::RoadList rlist(w->mStreetMap->getRoads(i));
      QGraphicsPathItem* intersection(createIntersection(i, rlist));
      mScene.addItem(intersection);
    }

    // Refresh all graphic items
    //refreshGraphics();
  }
}

void TrafficView::clearGraphics(const bool clearScene)
{
  foreach(VehicleItem *vi, mVehicleItems)
    mScene.removeItem(vi);
  mVehicleItems.clear();
  foreach(TrafficControlItem *tci, mTrafficControlItems)
    mScene.removeItem(tci);
  mTrafficControlItems.clear();
  if (clearScene)
    mScene.clear();
}


QGraphicsPathItem* TrafficView::createRoad(StreetMap::Road *road)
{
  RoadItem* r(new RoadItem());
  r->setFirst(road->first());
  r->setSecond(road->second());
  r->setLanesFirstSecond(road->numLanes(StreetMap::Road::Lane::FROM_FIRST_TO_SECOND));
  r->setLanesSecondFirst(road->numLanes(StreetMap::Road::Lane::FROM_SECOND_TO_FIRST));
  r->setRoadWidth(road->width());
  return r;
}

QGraphicsPathItem* TrafficView::createIntersection(StreetMap::Intersection *i,
                                                   const StreetMap::RoadList& roads)
{
  IntersectionItem* ret(new IntersectionItem());

  if (i)
  {
    ret->setIntersectionCenter(i->location());
    ret->setIntersectionName(i->name());
    ret->addRoads(roads);
  }

  return ret;
}

void TrafficView::wheelEvent(QWheelEvent *event)
{
  // Some fancy math to get the mouseWheel scroll/pan to work the way it does.
  // I should probably 'splain this a little at some point. And it still ain't
  // workin quite right...
  qreal panSpeedLimiter = width()*0.14;
  QPointF sceneCenterS = mapToScene(rect().center());
  QPointF eventPosS = mapToScene(event->pos());
  //QPointF sceneCenterWindow = mapToGlobal(rect().center());
  QPointF sceneCenterWindowG = mapToGlobal(rect().center());
  QPointF eventPosG = mapToGlobal(event->pos());
  QPointF windowCenterDelta = eventPosG - sceneCenterWindowG;

  //qDebug() << "SCENE: sccw=" << sceneCenterWindowG << "ep=" << eventPosG << "dc=" << windowCenterDelta;

  // Cap panning so the screen edge doesn't send us flying off into nowhere
  if (windowCenterDelta.x() > panSpeedLimiter)
    windowCenterDelta.setX(panSpeedLimiter);
  else if (windowCenterDelta.x() < -panSpeedLimiter)
    windowCenterDelta.setX(-panSpeedLimiter);
  if (windowCenterDelta.y() > panSpeedLimiter)
    windowCenterDelta.setY(panSpeedLimiter);
  else if (windowCenterDelta.y() < -panSpeedLimiter)
    windowCenterDelta.setY(-panSpeedLimiter);

  // Left pan doesn't seem to work all that well, same with up pan. Weird.
  //if (windowCenterDelta.x() < 0.0 && (event->delta() > 0))
    //windowCenterDelta.setX(windowCenterDelta.x()*1.5);

  qreal horizontalScale = windowCenterDelta.x() / (width()/2) * mZoomLevel;
  qreal verticalScale = windowCenterDelta.y() / (height()/2) * mZoomLevel;
  qreal newX = (eventPosS.x() - sceneCenterS.x()) * qAbs(horizontalScale);
  qreal newY = (eventPosS.y() - sceneCenterS.y()) * qAbs(verticalScale);

    //qDebug() << "SCENE: AFTER dc=" << windowCenterDelta
    //    << "nx=" << newX
    //    << "ny=" << newY
    //    << "pl=" << panSpeedLimiter;

  // Determine new center to pan the map as we zoom
  QPointF newCenter((sceneCenterS.x() + newX), (sceneCenterS.y() + newY));

  // Apply pan and zoom
  if (event->delta() > 0)
    zoom(1);
  else
    zoom(0);
  centerOn(newCenter);

  // Suppresses vert/horiz scrolling
  event->accept();
}

void TrafficView::showVectors(bool show)
{
  mShowVectors = show;
  foreach(VehicleItem* vi, mVehicleItems)
  {
    if (vi)
    {
      if (mShowLinesForSelectionOnly)
      {
        if (mSelectedVehicle && vi->toolTip() == mSelectedVehicle->name())
          vi->showVectors(mShowVectors);
        else
          vi->showVectors(false);

      }
      else
        vi->showVectors(mShowVectors);
    }
  }
}

void TrafficView::showForces(bool show)
{
  mShowForces = show;
  foreach(VehicleItem* vi, mVehicleItems)
  {
    if (vi)
    {
      if (mShowLinesForSelectionOnly)
      {
        if (mSelectedVehicle && vi->toolTip() == mSelectedVehicle->name())
          vi->showForces(mShowForces);
        else
          vi->showForces(false);

      }
      else
        vi->showForces(mShowForces);
    }
  }
}

void TrafficView::setSelectionOnly(const bool value)
{
  mShowLinesForSelectionOnly = value;
  showForces(mShowForces);
  showVectors(mShowVectors);
}


void TrafficView::setSelectedVehicle(Vehicle *vehicle)
{
  mSelectedVehicle = vehicle;
  showForces(mShowForces);
  showVectors(mShowVectors);
}

void TrafficView::zoom(bool zoomIn)
{
  if (zoomIn)
  {
    scale(1.2, 1.2);
    mZoomLevel*=0.97;
  }
  else
  {
    scale(1/1.2, 1/1.2);
    mZoomLevel*=1.03;
  }
}

void TrafficView::setSceneRectWithMargin(const float margin)
{
  QRectF r = mScene.itemsBoundingRect();
  r.setLeft(r.left()-margin);
  r.setTop(r.top()-margin);
  r.setBottom(r.bottom()+margin);
  r.setRight(r.right()+margin);
  setSceneRect(r);
}

void TrafficView::centerOnContent()
{
  // Give a little extra room around the borders so panning isn't locked
  // immediately around the edges of content, and we can pan past it.
  setSceneRectWithMargin();
  centerOn(sceneRect().center());
  resetTransform();
  mZoomLevel = 1.0f;
}


